package com.cheng.dataStructure.stack;

/**
 * @User Administrator
 * @Classname Calculator
 * @Project Data-structure-and-algorithm
 * @Description 栈实现表达式计算， 思路分析：
 * 1.定义一个索引index，来遍历表达式
 * 2.再定义两个栈，一个存放数字，为数栈，另一个存放运算符，为符号栈
 * 3.如果遍历的是数字，就直接存入数栈
 * 4.如果遍历出的是符号，分如下情况：
 *   4.1.如果符号栈为空，就直接入栈
 *   4.2.如果符号栈已有运算符，就进行比较，如果符号栈中的运算符优先级大于当前遍历出的运算符的优先级，
 *   就从数栈pop出两个数num1，num2（注：pop出的两个数有顺序，如：num2-num1），从符号栈中pop一个符号，进行运行，
 *   将的得到的结果入数栈，然后将当前遍历的运算符放入符号栈
 *   4.3.如果符号栈中的运算符优先级小于等于当前遍历出的运算符的优先级，就把当前运算符放入符号栈
 * 5.当表达式遍历完毕，就顺序的从数栈中pop出相应的数和符号，进行运行
 * 6.最后数栈中只有一个数字，就是表达式的值
 * @Author wpcheng
 * @Create 2021-07-19-10:52
 * @Version 1.0
 */
public class Calculator {
    public static void main(String[] args) {
        String expression = "6000+5*4-5";
        //创建数栈和符号栈
        ArrayStack2 numStack = new ArrayStack2(10);
        ArrayStack2 operStack = new ArrayStack2(10);
        //定义需要的相关变量
        int index = 0;
        int num1 = 0;
        int num2 = 0;
        int oper =0;
        int res = 0;
        char ch = ' ';//将每次遍历得到的数字或运算符保存到ch
        String keepNum = ""; //当数字是多位数时，用于拼接多位数
        //扫描expression表达式
        while(true){
            //依次得到表达式的每一个字符
            ch = expression.substring(index,index+1).charAt(0);
            //判断ch中是数字还是运算符
            if (operStack.isOper(ch)){ //如果是运算符
                //如果符号栈已有运算符，就进行比较，，
                if (!operStack.isEmpty()){
                    //如果符号栈中的运算符优先级大于当前遍历出的运算符的优先级，
                    if (operStack.priority(ch) <= operStack.priority(operStack.peek())){
                        //就从数栈pop出两个数num1，num2（注：pop出的两个数有顺序，如：num2-num1），从符号栈中pop一个符号，进行运算
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        oper = operStack.pop();
                        res = numStack.cal(num1,num2,oper);
                        //将的得到的结果入数栈
                        numStack.push(res);
                        //然后将当前遍历的运算符放入符号栈
                        operStack.push(ch);
                    }else{
                        //如果符号栈中的运算符优先级小于等于当前遍历出的运算符的优先级，就把当前运算符放入符号栈
                        operStack.push(ch);
                    }
                }else {
                    ////如果符号栈为空
                    operStack.push(ch);
                }
            }else {
                /*
                * ch如果是数字,不能直接入数栈，因为它可能是多位数,在处理多位数时，需要多遍历一下，
                * 如果是数就继续扫描,如果是运算符就入符号栈
                * */
                keepNum = keepNum + ch;
                //如果ch已经是表达式的最后一个，直接入数栈
                if (index == expression.length()-1){ //index下标从0开始
                    numStack.push(Integer.parseInt(keepNum));
                }else {
                    //判断index下一个是数字还说运算符
                    if (operStack.isOper(expression.substring(index+1,index+2).charAt(0))){
                        numStack.push(Integer.parseInt(keepNum));
                        keepNum = "";//释放keepNum
                    }
                }



            }
            //index继续向下遍历，并判断是否遍历到表达式的最后
            index++;
            if (index >= expression.length()){
                break;
            }
        }
        //当表达式遍历完毕，就顺序的从数栈和符号栈中pop出相应的数字和运算符进行运算
        while (true){
            //如果符号栈为空，表明数栈中只有一个数字，就是结果
            if (operStack.isEmpty()){
                break;
            }
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            res = numStack.cal(num1,num2,oper);
            numStack.push(res);
        }
        int res2 = numStack.pop();//将结果取出来
        System.out.printf("表达式 %s = %d",expression,res2);

    }
}


//定义一个栈ArrayStack2
class ArrayStack2{
    private int maxSize; //栈的大小
    private int[] stack; //数组
    private int top = -1; //栈顶，初始化为-1

    public ArrayStack2(int maxSize){
        this.maxSize = maxSize;
        stack = new int[maxSize];// 初始化栈的大小为maxSize
    }

    //栈满
    public boolean isFull(){
        return top == maxSize - 1;
    }
    //栈空
    public boolean isEmpty(){
        return top == -1;
    }
    //入栈
    public void push(int value){
        if (isFull()){
            System.out.println("栈满，无法添加");
            return;
        }
        top++;
        stack[top] = value;//把value放入栈中
    }
    //出栈
    public int pop(){
        if (isEmpty()){
            throw new RuntimeException("栈空，无法取出");
        }
        int value = stack[top];//从栈顶取出数据放入value中
        top--;
        return value;
    }
    //显示栈顶元素
    public int peek(){
        return stack[top];
    }

    //显示栈
    public void list(){
        if (isEmpty()){
            System.out.println("栈空");
            return;
        }
        //从栈顶开始遍历
        for (int i = top; i >= 0 ; i--) {
            System.out.printf("stack[%d]=%d\n" , i, stack[i]);
        }
    }
    //设置运算符的优先级
    public  int priority(int oper){
        if (oper == '*' || oper == '/'){ // * 和 / 的优先级为1
            return 1;
        }else if (oper == '+' || oper == '-'){  // + 和 - 的优先级为0
            return 0;
        }else { //只支持上面四种优先级
            return -1;
        }
    }
    //判断是不是一个运算符
    public boolean isOper(char val){
        return val == '+' || val == '-' || val == '*' || val == '/';
    }
    //计算方法
    public int cal(int num1, int num2, int oper){
        int res = 0;//用于存放结果
        switch (oper){
            case '+':
                res = num1 + num2;
                break;
            case '-':
                res = num2 - num1;
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                res = num2 / num1;
                break;
            default:
                break;
        }
        return res;
    }
}